package com.unidt.www.framework.common.util.http;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * @author cjw
 * @version 1.0.0
 */
@Slf4j
@Setter
@Getter
public class OkHttpUtils {


    /**
     * HTTPCLIENT连接池配置
     */
    private HttpClient HTTPCLIENT = null;
    private SSLContext sslcontext = null;
    private Map<String, Object> params = new HashMap<>();
    private Map<String, String> heads = new HashMap<>();
    private String url;
    private String contentType;
    private String charset = "UTF-8";

    /**
     * in milliseconds
     */
    private final static Integer CONNECT_TIMEOUT = 5000;


    public static OkHttpUtils builder() {
        return new OkHttpUtils();
    }

    public OkHttpUtils url(String url) {
        this.url = url;
        return this;
    }

    public OkHttpUtils contentType(String contentType) {
        this.contentType = contentType;
        return this;
    }

    public OkHttpUtils charset(String charset) {
        this.charset = charset;
        return this;
    }

    public OkHttpUtils addParam(String key, Object value) {
        this.params.put(key, value);
        return this;
    }
    public OkHttpUtils addParam(Map<String, Object> params) {
        this.params=params;
        return this;
    }
    public OkHttpUtils addHeads(String key, String value) {
        this.heads.put(key, value);
        return this;
    }

    public String get() {
        return getRequest(url, heads, params);
    }

    public <T> T get(Class<T> clazz) {
        String json = getRequest(url, heads, params);
        return JSONObject.parseObject(json, clazz);
    }

    public String postJson() {
        return postJson(JSON.toJSONString(params));
    }


    public <T> T postJson(Class<T> clazz) {
        String json = postJson(JSON.toJSONString(params));
        return JSONObject.parseObject(json, clazz);
    }

    public String postJson(String json) {
        return requestBodyJson(url, heads, json, contentType == null ? "application/json; charset=utf-8" : contentType, "POST");
    }

    public <T> T postJson(String json, Class<T> clazz) {
        String jsonObj = requestBodyJson(url, heads, json, contentType == null ? "application/json; charset=utf-8" : contentType, "POST");
        return JSONObject.parseObject(jsonObj, clazz);
    }


    public String postForm() {
        return requestBodyForm(url, heads, params, "POST");
    }


    public <T> T postForm(Class<T> clazz) {
        String json = requestBodyForm(url, heads, params, "POST");
        return JSONObject.parseObject(json, clazz);
    }

    public String putJson() {
        return putJson(JSON.toJSONString(params));
    }

    public <T> T putJson(Class<T> clazz) {
        String json = putJson(JSON.toJSONString(params));
        return JSONObject.parseObject(json, clazz);
    }

    public String putJson(String json) {
        return requestBodyJson(url, heads, json, contentType == null ? "application/json; charset=utf-8" : contentType, "PUT");
    }

    public <T> T putJson(String json, Class<T> clazz) {
        String jsonObj = requestBodyJson(url, heads, json, contentType == null ? "application/json; charset=utf-8" : contentType, "PUT");
        return JSONObject.parseObject(jsonObj, clazz);
    }

    public String putForm() {
        return requestBodyForm(url, heads, params, "PUT");
    }

    public <T> T putForm(Class<T> clazz) {
        String jsonObj = requestBodyForm(url, heads, params, "PUT");
        return JSONObject.parseObject(jsonObj, clazz);
    }


    /**
     * @param url 请求地址
     * @return String
     */
    private String getRequest(String url, Map<String, String> headers, Map<String, Object> params) {
        if (params != null && !params.isEmpty()) {
            StringBuffer paramString = new StringBuffer();
            if (url.contains("?")) {
                paramString.append("&");
            } else {
                paramString.append("?");
            }
            params.forEach((k, v) -> paramString.append(k + "=" + v + "&"));
            url = url + StringUtils.removeEnd(paramString.toString(), "&");
        }
        Request.Builder builder = new Request.Builder().url(url);
        return builderRequest(builder, headers, "GET", null);
    }

    /**
     * @param url 请求地址
     * @param json 请求参数,body体是表单
     * @param headers 请求头信息
     * @return String
     */
    private String requestBodyJson(String url, Map<String, String> headers, String json, String contentType, String method) {
        Request.Builder builder = new Request.Builder().url(url);
        MediaType mediaType = MediaType.parse(contentType);
        RequestBody body = RequestBody.create(mediaType, json);
        return builderRequest(builder, headers, method, body);
    }


    /**
     * @param url 请求地址
     * @param params 请求参数,body体是表单
     * @param headers 请求头信息
     * @return String
     */
    private String requestBodyForm(String url, Map<String, String> headers, Map<String, Object> params, String method) {
        Request.Builder builder = new Request.Builder().url(url);
        MultipartBody.Builder bodyBuild = new MultipartBody.Builder()
                .setType(MultipartBody.FORM);
        if (params != null && !params.isEmpty()) {
            params.forEach((k, v) -> bodyBuild.addFormDataPart(k, v.toString()));
        }
        return builderRequest(builder, headers, method, bodyBuild.build());
    }


    private String builderRequest(Request.Builder builder, Map<String, String> headers, String method, RequestBody body) {
        Request.Builder builderMethod = builder.method(method, body);
        if (headers != null && !headers.isEmpty()) {
            headers.forEach((k, v) -> builderMethod.addHeader(k, v));
        }
        String result;
        try {
            Response execute = getOkHttpClient().newCall(builderMethod.build()).execute();
            result = execute.body().string();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("当前请求失败:{}", e.getMessage());
            result = e.getMessage();
        }
        return result;
    }


    /**
     * @param @return
     * @return HttpClient
     */
    private OkHttpClient getOkHttpClient() {
        //https连接初始化
        if (sslcontext == null) {
            initSSLContext();
        }
        SSLSocketFactory socketFactory = sslcontext.getSocketFactory();
        OkHttpClient OK_CLIENT = new OkHttpClient().newBuilder()
                .connectTimeout(60L, TimeUnit.SECONDS)
                .readTimeout(30L, TimeUnit.SECONDS)
                .writeTimeout(60L, TimeUnit.SECONDS)
                .sslSocketFactory(socketFactory, new miTM())
                .hostnameVerifier((String urlHostName, SSLSession session) -> true)
                .build();
        return OK_CLIENT;
    }


    /**
     * 初始化ssl.
     * @return the ssl context
     */
    public OkHttpUtils initSSLContext() {
        initSSLContext(null, null);
        return this;
    }

    /**
     * 初始化ssl.
     * @param keyPath 绝对路径
     * @param params 参数
     * @return the ssl context
     */
    public OkHttpUtils initSSLContext(String keyPath, List<String> params) {
        if (keyPath == null || params == null) {
            javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
            javax.net.ssl.TrustManager tm = new miTM();
            trustAllCerts[0] = tm;
            SSLContext sc = null;
            try {
                sc = SSLContext.getInstance("SSL");
                sc.init(null, trustAllCerts, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            sslcontext = sc;
            return this;
        }
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //P12文件目录 证书路径
            FileInputStream in = new FileInputStream(keyPath);
            SSLContextBuilder sslContextBuilder = SSLContexts.custom();
            for (String param : params) {
                keyStore.load(in, param.toCharArray());
                sslContextBuilder.loadKeyMaterial(keyStore, param.toCharArray());
            }
            in.close();
            sslcontext = sslContextBuilder.build();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return this;
    }


    static class miTM implements javax.net.ssl.TrustManager,
            javax.net.ssl.X509TrustManager {
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[0];
        }

        public boolean isServerTrusted(
                java.security.cert.X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(
                java.security.cert.X509Certificate[] certs) {
            return true;
        }

        @Override
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }

        @Override
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] certs, String authType)
                throws java.security.cert.CertificateException {
            return;
        }
    }


}
